home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / audacity / nyquist / sal.lsp < prev    next >
Encoding:
Text File  |  2010-09-21  |  20.1 KB  |  556 lines

  1. ;;; **********************************************************************
  2. ;;; Copyright (C) 2006 Rick Taube
  3. ;;; This program is free software; you can redistribute it and/or   
  4. ;;; modify it under the terms of the Lisp Lesser Gnu Public License.
  5. ;;; See http://www.cliki.net/LLGPL for the text of this agreement.
  6. ;;; **********************************************************************
  7.  
  8. ;;; $Revision: 1.2 $
  9. ;;; $Date: 2009-03-05 17:42:25 $
  10.  
  11. ;; DATA STRUCTURES AND ALGORITHMS (for sal.lsp and parse.lsp)
  12. ;;
  13. ;; TOKENIZE converts source language (a string) into a list of tokens
  14. ;;    each token is represented as follows:
  15. ;;    (:TOKEN <type> <string> <start> <info> <lisp>)
  16. ;;    where <type> is one of:
  17. ;;        :id -- an identifier
  18. ;;        :lp -- left paren
  19. ;;        :rp -- right paren
  20. ;;        :+, etc. -- operators
  21. ;;        :int -- an integer
  22. ;;        :float -- a float
  23. ;;        :print, etc. -- a reserved word
  24. ;;    <string> is the source string for the token
  25. ;;    <start> is the column of the string
  26. ;;    <info> and <lisp> are ??
  27. ;; Tokenize uses a list of reserved words extracted from terminals in
  28. ;;    the grammar. Each reserved word has an associated token type, but
  29. ;;    all other identifiers are simply of type :ID.
  30. ;;
  31. ;; *** WHY REWRITE THE ORIGINAL PARSER? ***
  32. ;; Originally, the code interpreted a grammar using a recursive pattern
  33. ;; matcher, but XLISP does not have a huge stack and there were
  34. ;; stack overflow problems because even relatively small expressions
  35. ;; went through a very deep nesting of productions. E.g. 
  36. ;; "print note(between(30,odds(.5, 60, 90)))" 0 t nil))" was at recursion
  37. ;; level 46 when the stack overflowed. The stack depth is 2000 or 4000,
  38. ;; but all locals and parameters get pushed here, so since PARSE is the
  39. ;; recursive function and it has lots of parameters and locals, it appears
  40. ;; to use 80 elements in the stack per call.
  41. ;; *** END ***
  42. ;;
  43. ;; The grammar for the recursive descent parser:
  44. ;;   note: [ <x> ] means optional <x>, <x>* means 0 or more of <x>
  45. ;;
  46. ;; <number> = <int> | <float>
  47. ;; <atom> = <int> | <float> | <id> | <bool>
  48. ;; <list> = { <elt>* }
  49. ;; <elt> = <atom> | <list> | <string>
  50. ;; <aref> = <id> <lb> <pargs> <rb>
  51. ;; <ifexpr> = ? "(" <sexpr> , <sexpr> [ , <sexpr> ] ")"
  52. ;; <funcall> = <id> <funargs>
  53. ;; <funargs> = "(" [ <args> ] ")"
  54. ;; <args> =  <arg> [ , <arg> ]*
  55. ;; <arg> = <sexpr> | <key> <sexpr>
  56. ;; <op> = + | - | "*" | / | % | ^ | = | != |
  57. ;;        "<" | ">" | "<=" | ">=" | ~= | ! | & | "|"
  58. ;; <mexpr> = <term> [ <op> <term> ]*
  59. ;; <term> = <-> <term> | <!> <term> | "(" <mexpr> ")" |
  60. ;;          <ifexpr> | <funcall> | <aref> | <atom> | <list> | <string>
  61. ;; <sexpr> = <mexpr> | <object> | class
  62. ;; <top> = <command> | <block> | <conditional> | <assignment> | <loop> | <exec>
  63. ;; <exec> = exec <sexpr>
  64. ;; <command> = <define-cmd> | <file-cmd> | <output>
  65. ;; <define-cmd> = define <declaration>
  66. ;; <declaration> = <vardecl> | <fundecl>
  67. ;; <vardecl> = variable <bindings>
  68. ;; <bindings> = <bind> [ , <bind> ]*
  69. ;; <bind> = <id> [ <=> <sexpr> ]
  70. ;; <fundecl> = <function> <id> "(" [ <parms> ] ")" <statement>
  71. ;; <parms> = <parm> [ , <parm> ]*
  72. ;;  this is new: key: expression for keyword parameter
  73. ;; <parm> = <id> | <key> [ <sexpr> ] 
  74. ;; <statement> = <block> | <conditional> | <assignment> |
  75. ;;               <output-stmt> <loop-stmt> <return-from> | <exec>
  76. ;; <block> = begin [ with <bindings> [ <statement> ]* end
  77. ;; <conditional> = if <sexpr> then [ <statement> ] [ else <statement> ] |
  78. ;;                 when <sexpr> <statement> | unless <sexpr> <statement>
  79. ;; <assignment> = set <assign> [ , <assign> ]*
  80. ;; <assign> = ( <aref> | <id> ) <assigner> <sexpr>
  81. ;; <assigner> = = | += | *= | &= | @= | ^= | "<=" | ">="
  82. ;; <file-cmd> = <load-cmd> | chdir <pathref> | 
  83. ;;              system <pathref> | play <sexpr>
  84. ;; (note: system was removed)
  85. ;; <load-cmd> = load <pathref> [ , <key> <sexpr> ]* 
  86. ;; <pathref> = <string> | <id>
  87. ;; <output-stmt> = print <sexpr> [ , <sexpr> ]* |
  88. ;;                 output <sexpr>
  89. ;; <loop-stmt> = loop [ with <bindings> ] [ <stepping> ]* 
  90. ;;               [ <termination> ]* [ <statement> ]+
  91. ;;               [ finally <statement> ] end
  92. ;; <stepping> = repeat <sexpr> |
  93. ;;              for <id> = <sexpr> [ then <sexpr> ] |
  94. ;;              for <id> in <sexpr> |
  95. ;;              for <id> over <sexpr> [ by <sexpr> ] |
  96. ;;              for <id> [ from <sexpr> ]
  97. ;;                       [ ( below | to | above | downto ) <sexpr> ]
  98. ;;                       [ by <sexpr> ] |
  99. ;; <termination> = while <sexpr> | until <sexpr>
  100. ;; <return-from> = return <sexpr>
  101.  
  102. ;(in-package cm)
  103.  
  104. ; (progn (cd "/Lisp/sal/") (load "parse.lisp") (load "sal.lisp"))
  105.  
  106. (setfn defconstant setf)
  107. (setfn defparameter setf)
  108. (setfn defmethod defun)
  109. (setfn defvar setf)
  110. (setfn values list)
  111. (if (not (boundp '*sal-secondary-prompt*))
  112.     (setf *sal-secondary-prompt* t))
  113. (if (not (boundp '*sal-xlispbreak*))
  114.     (setf *sal-xlispbreak* nil))
  115.  
  116. (defun sal-trace-enter (fn &optional argvals argnames)
  117.   (push (list fn *sal-line* argvals argnames) *sal-call-stack*))
  118.  
  119. (defun sal-trace-exit ()
  120.   (setf *sal-line* (second (car *sal-call-stack*)))
  121.   (pop *sal-call-stack*))
  122.  
  123. ;; SAL-RETURN-FROM is generated by Sal compiler and
  124. ;;  performs a return as well as a sal-trace-exit()
  125. ;;
  126. (defmacro sal-return-from (fn val)
  127.   `(prog ((sal:return-value ,val))
  128.      (setf *sal-line* (second (car *sal-call-stack*)))
  129.      (pop *sal-call-stack*)
  130.      (return-from ,fn sal:return-value)))
  131.  
  132.  
  133. (setf *sal-traceback* t)
  134.  
  135.  
  136. (defun sal-traceback (&optional (file t) 
  137.                       &aux comma name names line)
  138.   (format file "Call traceback:~%")
  139.   (setf line *sal-line*)
  140.   (dolist (frame *sal-call-stack*)
  141.     (setf comma "")
  142.     (format file "    ~A" (car frame))
  143.     (cond ((symbolp (car frame))
  144.            (format file "(")
  145.            (setf names (cadddr frame))
  146.            (dolist (arg (caddr frame))
  147.              (setf name (car names))
  148.              (format file "~A~%        ~A = ~A" comma name arg)
  149.              (setf names (cdr names))
  150.              (setf comma ","))
  151.            (format file ") at line ~A~%" line)
  152.            (setf line (second frame)))
  153.           (t 
  154.            (format file "~%")))))
  155.  
  156.  
  157. '(defmacro defgrammer (sym rules &rest args)
  158.   `(defparameter ,sym
  159.      (make-grammer :rules ',rules ,@args)))
  160.  
  161. '(defun make-grammer (&key rules literals)
  162.   (let ((g (list 'a-grammer rules literals)))
  163.     (grammer-initialize g)
  164.     g))
  165.  
  166. '(defmethod grammer-initialize (obj)
  167.   (let (xlist)
  168.     ;; each literal is (:name "name")
  169.     (cond ((grammer-literals obj)
  170.            (dolist (x (grammer-literals obj))
  171.              (cond ((consp x)
  172.                     (push x xlist))
  173.                    (t
  174.                     (push (list (string->keyword (string-upcase (string x)))
  175.                                 (string-downcase (string x)))
  176.                           xlist)))))
  177.           (t
  178.            (dolist (x (grammer-rules obj))
  179.              (cond ((terminal-rule? x)
  180.                     (push (list (car x)
  181.                                 (string-downcase (subseq (string (car x)) 1)))
  182.                           xlist))))))
  183.     (set-grammer-literals obj (reverse xlist))))
  184.  
  185. '(setfn grammer-rules cadr)
  186. '(setfn grammer-literals caddr)
  187. '(defun set-grammer-literals (obj val)
  188.   (setf (car (cddr obj)) val))
  189. '(defun is-grammer (obj) (and (consp obj) (eq (car obj) 'a-grammer)))
  190.  
  191. (defun string->keyword (str)
  192.   (intern (strcat ":" (string-upcase str))))
  193.  
  194. (defun terminal-rule? (rule)
  195.   (or (null (cdr rule)) (not (cadr rule))))
  196.  
  197. (load "sal-parse.lsp" :verbose nil)
  198.  
  199. (defparameter *sal-print-list* t)
  200.  
  201. (defun sal-printer (x &key (stream *standard-output*) (add-space t))
  202.   (let ((*print-case* ':downcase))
  203.     (cond ((and (consp x) *sal-print-list*)
  204.        (write-char #\{ stream)
  205.        (do ((items x (cdr items)))
  206.                ((null items))
  207.           (sal-printer (car items) :stream stream
  208.                                        :add-space (cdr items))
  209.           (cond ((cdr items)
  210.                      (cond ((not (consp (cdr items)))
  211.                             (princ "<list not well-formed> " stream)
  212.                             (sal-printer (cdr items) :stream stream :add-space nil)
  213.                             (setf items nil))))))
  214.        (write-char #\} stream))
  215.       ((not x)     (princ "#f" stream) )
  216.       ((eq x t)    (princ "#t" stream))
  217.       (t           (princ x stream)))
  218.     (if add-space (write-char #\space stream))))
  219.  
  220. (defparameter *sal-printer* #'sal-printer)
  221.  
  222. (defun sal-message (string &rest args)
  223.   (format t "~&; ")
  224.   (apply #'format t string args))
  225.  
  226.  
  227. (defun sal-print (&rest args)
  228.   (terpri)
  229.   (mapc *sal-printer* args)
  230.   (values))
  231.  
  232. (defmacro keyword (sym)
  233.   `(str-to-keyword (symbol-name ',sym)))
  234.  
  235. (defun plus (&rest nums)
  236.   (apply #'+ nums))
  237.  
  238. (defun minus (num &rest nums)
  239.   (apply #'- num nums))
  240.  
  241. (defun times (&rest nums)
  242.   (apply #'* nums))
  243.  
  244. (defun divide (num &rest nums)
  245.   (apply #'/ num nums))
  246.  
  247. ;; implementation of infix "!=" operator
  248. (defun not-eql (x y)
  249.   (not (eql x y)))
  250.  
  251. ; dir "*.*
  252. ; chdir
  253. ; load "rts.sys"
  254.  
  255. (defun sal-chdir ( dir)
  256.   (cd (expand-path-name dir))
  257.   (sal-message "Directory: ~A" (pwd))
  258.   (values))
  259.  
  260. ;;; sigh, not all lisps support ~/ directory components.
  261.  
  262. (defun expand-path-name (path &optional absolute?)
  263.   (let ((dir (pathname-directory path)))
  264.     (flet ((curdir ()
  265.          (truename 
  266.           (make-pathname :directory
  267.                  (pathname-directory
  268.                   *default-pathname-defaults*)))))
  269.       (cond ((null dir)
  270.          (if (equal path "~") 
  271.          (namestring (user-homedir-pathname))
  272.          (if absolute? 
  273.              (namestring (merge-pathnames path (curdir)))
  274.              (namestring path))))
  275.         ((eql (car dir) ':absolute)
  276.          (namestring path))
  277.         (t
  278.          (let* ((tok (second dir))
  279.             (len (length tok)))
  280.            (if (char= (char tok 0) #\~)
  281.            (let ((uhd (pathname-directory (user-homedir-pathname))))
  282.              (if (= len 1)
  283.              (namestring
  284.               (make-pathname :directory (append uhd (cddr dir))
  285.                      :defaults path))
  286.              (namestring
  287.               (make-pathname :directory
  288.                      (append (butlast uhd)
  289.                          (list (subseq tok 1))
  290.                          (cddr dir))
  291.                      :defaults path))))
  292.            (if absolute?
  293.                (namestring (merge-pathnames  path (curdir)))
  294.                (namestring path)))))))))
  295.  
  296.  
  297. (defun sal-load (filename &key (verbose t) print)
  298.   (progv '(*sal-input-file-name*) (list filename)
  299.     (prog (file extended-name)
  300.       ;; first try to load exact name
  301.       (cond ((setf file (open filename))
  302.              (close file) ;; found it: close it and load it
  303.              (return (generic-loader filename verbose print))))
  304.       ;; try to load name with ".sal" or ".lsp"
  305.       (cond ((string-search "." filename) ; already has extension
  306.              nil) ; don't try to add another extension
  307.             ((setf file (open (strcat filename ".sal")))
  308.              (close file)
  309.              (return (sal-loader (strcat filename ".sal")
  310.                                  :verbose verbose :print print)))
  311.             ((setf file (open (strcat filename ".lsp")))
  312.              (close file)
  313.              (return (lisp-loader filename :verbose verbose :print print))))
  314.       ;; search for file as is or with ".lsp" on path
  315.       (setf fullpath (find-in-xlisp-path filename))
  316.       (cond ((and (not fullpath) ; search for file.sal on path
  317.                   (not (string-search "." filename))) ; no extension yet
  318.              (setf fullpath (find-in-xlisp-path (strcat filename ".sal")))))
  319.       (cond ((null fullpath)
  320.              (format t "sal-load: could not find ~A~%" filename))
  321.             (t
  322.              (return (generic-loader filename verbose print)))))))
  323.  
  324.  
  325. ;; GENERIC-LOADER -- load a sal or lsp file based on extension
  326. ;;
  327. ;; assumes that file exists, and if no .sal extension, type is Lisp
  328. ;;
  329. (defun generic-loader (fullpath verbose print)
  330.   (cond ((has-extension fullpath ".sal")
  331.          (sal-loader fullpath :verbose verbose :print print))
  332.         (t
  333.          (lisp-loader fullpath :verbose verbose :print print))))
  334.  
  335. #|
  336. (defun sal-load (filename &key (verbose t) print)
  337.   (progv '(*sal-input-file-name*) (list filename)
  338.     (let (file extended-name)
  339.       (cond ((has-extension filename ".sal")
  340.              (sal-loader filename :verbose verbose :print print))
  341.             ((has-extension filename ".lsp")
  342.              (lisp-load filename :verbose verbose :print print))
  343.             ;; see if we can just open the exact filename and load it
  344.             ((setf file (open filename))
  345.              (close file)
  346.              (lisp-load filename :verbose verbose :print print))
  347.             ;; if not, then try loading file.sal and file.lsp
  348.             ((setf file (open (setf *sal-input-file-name*
  349.                                     (strcat filename ".sal"))))
  350.              (close file)
  351.              (sal-loader *sal-input-file-name* :verbose verbose :print print))
  352.             ((setf file (open (setf *sal-input-file-name* 
  353.                                     (strcat filename ".lsp"))))
  354.              (close file)
  355.              (lisp-load *sal-input-file-name* :verbose verbose :print print))
  356.             (t
  357.              (format t "sal-load: could not find ~A~%" filename))))))
  358. |#
  359.  
  360. (defun lisp-loader (filename &key (verbose t) print)
  361.   (if (load filename :verbose verbose :print print)
  362.       nil ; be quiet if things work ok
  363.       (format t "error loading lisp file ~A~%" filename)))
  364.  
  365.  
  366. (defun has-extension (filename ext)
  367.   (let ((loc (string-search ext filename
  368.                             :start (max 0 (- (length filename)
  369.                                              (length ext))))))
  370.     (not (null loc)))) ; coerce to t or nil
  371.     
  372.  
  373. (defmacro sal-at (s x) (list 'at x s))
  374. (defmacro sal-at-abs (s x) (list 'at-abs x s))
  375. (defmacro sal-stretch (s x) (list 'stretch x s))
  376. (defmacro sal-stretch-abs (s x) (list 'stretch-abs x s))
  377.  
  378. ;; splice every pair of lines
  379. (defun strcat-pairs (lines)
  380.   (let (rslt)
  381.     (while lines
  382.       (push (strcat (car lines) (cadr lines)) rslt)
  383.       (setf lines (cddr lines)))
  384.     (reverse rslt)))
  385.  
  386.  
  387. (defun strcat-list (lines)
  388.   ;; like (apply 'strcat lines), but does not use a lot of stack
  389.   ;; When there are too many lines, XLISP will overflow the stack
  390.   ;; because args go on the stack.
  391.   (let (r)
  392.     (while (> (setf len (length lines)) 1)
  393.       (if (oddp len) (setf lines (cons "" lines)))
  394.       (setf lines (strcat-pairs lines)))
  395.     ; if an empty list, return "", else list has one string: return it
  396.     (if (null lines) "" (car lines))))
  397.  
  398.  
  399. (defun sal-loader (filename &key verbose print)
  400.   (let ((input "") (file (open filename)) line lines)
  401.     (cond (file
  402.            (push filename *loadingfiles*)
  403.            (while (setf line (read-line file))
  404.             (push line lines)
  405.             (push "\n" lines))
  406.            (close file)
  407.            (setf input (strcat-list (reverse lines)))
  408.            (sal-trace-enter (strcat "Loading " filename))
  409.            (sal-compile input t t filename)
  410.            (pop *loadingfiles*)
  411.            (sal-trace-exit))
  412.           (t
  413.            (format t "error loading SAL file ~A~%" filename)))))
  414.  
  415.  
  416. ; SYSTEM command is not implemented
  417. ;(defun sal-system (sys &rest pairs)
  418. ;  (apply #'use-system sys pairs))
  419.  
  420.  
  421. (defun load-sal-file (file)
  422.   (with-open-file (f file :direction :input)
  423.     (let ((input (make-array '(512) :element-type 'character
  424.                  :fill-pointer 0 :adjustable t)))
  425.       (loop with flag
  426.      for char = (read-char f nil ':eof)
  427.      until (or flag (eql char ':eof))
  428.      do
  429.        (when (char= char #\;)
  430.          (loop do (setq char (read-char f nil :eof))
  431.         until (or (eql char :eof)
  432.               (char= char #\newline))))
  433.        (unless (eql char ':eof)
  434.          (vector-push-extend char input)))
  435.       (sal input :pattern :command-sequence))))
  436.  
  437.  
  438. (defmacro sal-play (snd)
  439.   (if (stringp snd) `(play-file ,snd)
  440.                     `(play ,snd)))
  441.  
  442.  
  443. (if (not (boundp '*sal-compiler-debug*))
  444.     (setf *sal-compiler-debug* nil))
  445.  
  446.  
  447. (defmacro sal-simrep (variable iterations body)
  448.   `(simrep (,variable ,iterations) ,body))
  449.  
  450.  
  451. (defmacro sal-seqrep (variable iterations body)
  452.   `(seqrep (,variable ,iterations) ,body))
  453.  
  454.  
  455. ;; function called in sal programs to exit the sal read-compile-run-print loop
  456. (defun sal-exit () (setf *sal-exit* t))
  457.  
  458. ;; read-eval-print loop for sal commands
  459. (defun sal ()
  460.   (progv '(*breakenable* *tracenable* *sal-exit*)
  461.          (list *sal-xlispbreak* *sal-xlispbreak* nil)
  462.     (let (input line)
  463.       (setf *sal-call-stack* nil)
  464.       (read-line) ; read the newline after the one the user 
  465.                   ; typed to invoke this fn
  466.       (princ "Entering SAL mode ...\n");
  467.       (while (not *sal-exit*)
  468.         (princ "\nSAL> ")
  469.         (sal-trace-enter "SAL top-level command interpreter")
  470.         ;; get input terminated by two returns
  471.         (setf input "")
  472.         (while (> (length (setf line (read-line))) 0)
  473.           (if *sal-secondary-prompt* (princ " ... "))
  474.           (setf input (strcat input "\n" line)))
  475.         ;; input may have an extra return, remaining from previous read
  476.         ;; if so, trim it because it affects line count in error messages
  477.         (if (and (> (length input) 0) (char= (char input 0) #\newline))
  478.             (setf input (subseq input 1)))
  479.         (sal-compile input t nil "<console>")
  480.         (sal-trace-exit))
  481.       (princ "Returning to Lisp ...\n")
  482.       t ; return value
  483.       )))
  484.  
  485.  
  486. (defun sal-error-output (stack)
  487.   (if *sal-traceback* (sal-traceback))
  488.   (setf *sal-call-stack* stack)) ;; clear the stack
  489.  
  490. ;; SAL-COMPILE -- translate string or token list to lisp and eval
  491. ;;
  492. ;; input is either a string or a token list
  493. ;; eval-flag tells whether to evaluate the program or return the lisp
  494. ;; multiple-statements tells whether the input can contain multiple
  495. ;;   top-level units (e.g. from a file) or just one (from command line)
  496. ;; returns:
  497. ;;   if eval-flag, then nothing is returned
  498. ;;   otherwise, returns nil if an error is encountered
  499. ;;   otherwise, returns a list (PROGN p1 p2 p3 ...) where pn are lisp
  500. ;;      expressions
  501. ;;
  502. (defun sal-compile (input eval-flag multiple-statements filename)
  503.   ;; save some globals because eval could call back recursively
  504.   (progv '(*sal-tokens* *sal-input* *sal-input-text*) '(nil nil nil)
  505.     (let (output remainder rslt stack)
  506.       (setf stack *sal-call-stack*)
  507.       ;; if first input char is "(", then eval as a lisp expression:
  508.       ;(display "sal-compile" input)
  509.       (cond ((input-starts-with-open-paren input)
  510.              ;(print "input is lisp expression")
  511.              (errset
  512.               (print (eval (read (make-string-input-stream input)))) t))
  513.             (t ;; compile SAL expression(s):
  514.              (loop
  515.                 (setf output (sal-parse nil nil input multiple-statements 
  516.                                         filename))
  517.                 (cond ((first output) ; successful parse
  518.                        (setf remainder *sal-tokens*)
  519.                        (setf output (second output))
  520.                        (when *sal-compiler-debug*
  521.                          (terpri)
  522.                          (pprint output))
  523.                        (cond (eval-flag ;; evaluate the compiled code
  524.                               (cond ((null (errset (eval output) t))
  525.                                      (sal-error-output stack)
  526.                                      (return)))) ;; stop on error
  527.                              (t
  528.                               (push output rslt)))
  529.                                         ;(display "sal-compile after eval" 
  530.                                         ;         remainder *sal-tokens*)
  531.                        ;; if there are statements left over, maybe compile again
  532.                        (cond ((and multiple-statements remainder)
  533.                               ;; move remainder to input and iterate
  534.                               (setf input remainder))
  535.                              ;; see if we've compiled everything
  536.                              ((and (not eval-flag) (not remainder))
  537.                               (return (cons 'progn (reverse rslt))))
  538.                              ;; if eval but no more input, return
  539.                              ((not remainder)
  540.                               (return))))
  541.                       (t ; error encountered
  542.                        (return)))))))))
  543.  
  544. ;; SAL just evaluates lisp expression if it starts with open-paren,
  545. ;; but sometimes reader reads previous newline(s), so here we
  546. ;; trim off initial newlines and check if first non-newline is open-paren
  547. (defun input-starts-with-open-paren (input)
  548.   (let ((i 0))
  549.     (while (and (stringp input)
  550.                 (> (length input) i)
  551.                 (eq (char input i) #\newline))
  552.       (incf i))
  553.     (and (stringp input)
  554.          (> (length input) i)
  555.          (eq (char input i) #\())))
  556.